home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-09-02 | 23.3 KB | 726 lines | [TEXT/CCL2] |
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; av-graphics.lisp
- ;;;
- ;;; Graphics for the AV Parser for MCL 2.0ß1p3.
- ;;;
-
- (defpackage :graphics (:use :ccl)
- (:export ))
- (require :pict-scrolling-windows)
-
- (defvar *the-view* nil "Dynamically bound to the current scroller view being drawn in")
-
- (defparameter *tree-window* nil "Window for drawing trees")
- (defparameter *avm-window* nil "Window for drawing avms")
-
- ;;; utilities
-
- (defun font-size (view)
- "Current font size of window that view is in"
- (rref (wptr view) :grafport.txSize))
-
- (defun line-ascent ()
- "Ascent of font in current grafport"
- (rlet ((FontInfo :FontInfo))
- (#_GetFontInfo FontInfo)
- (rref FontInfo :FontInfo.ascent)))
-
- (defun line-descent ()
- "Descent of font in current grafport"
- (rlet ((FontInfo :FontInfo))
- (#_GetFontInfo FontInfo)
- (rref FontInfo :FontInfo.descent)))
-
- (defun line-leading ()
- "Leading of font in current grafport"
- (rlet ((FontInfo :FontInfo))
- (#_GetFontInfo FontInfo)
- (rref FontInfo :FontInfo.leading)))
-
- (defun line-height ()
- "Sum of ascent, descent and leading of font in current grafport"
- (rlet ((FontInfo :FontInfo))
- (#_GetFontInfo FontInfo)
- (+ (rref FontInfo :FontInfo.ascent)
- (rref FontInfo :FontInfo.descent)
- ; (rref FontInfo :FontInfo.leading)
- )))
-
- ;;; class definitions for graphic-view and graphic-window
- ;;;
-
- (defclass graphic-view (pict-scroller)
- ((selectable-p :accessor selectable-p :initarg :selectable-p :initform nil)
- (top-object :accessor top-object :initform nil)
- (selected-object :accessor selected-object :initform nil)
- (graphic-generation :accessor graphic-generation :initform 0)))
-
- (defclass graphic-window (pict-scrolling-window)
- ((scroller-class :allocation :class
- :initform 'graphic-view)))
-
- ;;; methods for graphic-view and graphic-window
- ;;;
-
- (defmethod border-h ((view graphic-view))
- 10)
-
- (defmethod border-v ((view graphic-view))
- 10)
-
- (defmethod view-close ((view graphic-view))
- (setf (top-object view) nil)
- (call-next-method))
-
- (defmethod view-draw-contents ((view graphic-view))
- (call-next-method)
- (let ((*the-view* view)
- (selected-object (selected-object view)))
- (when selected-object
- (with-focused-view view
- (highlight selected-object)))))
-
- (defmethod view-click-event-handler ((view graphic-view) where)
- (let ((*the-view* view)
- (selected-object (selected-object view))
- (top-object (top-object view)))
- (with-focused-view view
- (when selected-object
- (deselect selected-object)
- (setf (selected-object view) nil))
- (when (and (selectable-p view) top-object)
- (let ((new-selected-object (click-event-handler top-object where)))
- (when new-selected-object
- (setf (selected-object view) new-selected-object)
- (select new-selected-object)))))))
-
-
- (defmethod draw-object ((window graphic-window) object &key selectable-p)
- (draw-object (scroller window) object :selectable-p selectable-p))
-
- (defmethod draw-object ((view graphic-view) object &key selectable-p)
- (setf (selected-object view) nil)
- (erase-view view)
- (incf (graphic-generation view))
- (with-focused-view view
- (let* ((*the-view* view) ; dynamically bind *the-view* to the view being laid out
- (border-h (border-h view))
- (border-v (border-v view))
- (size-h (size-h object))
- (size-v (size-v object)))
- (with-pict-view view (make-point (+ size-h (* 2 border-h))
- (+ size-v (* 2 border-v)))
- (draw object border-h border-v))))
- (setf (selectable-p view) selectable-p)
- (setf (top-object view) object))
-
- (defmethod set-view-font-codes ((window graphic-window) old-ff old-ms &optional ff-mask ms-mask)
- (declare (ignore old-ff old-ms ff-mask ms-mask))
- (call-next-method)
- (when (slot-boundp window 'scroller)
- (let* ((scroller (scroller window))
- (top-object (top-object scroller))
- (selectable-p (selectable-p scroller))
- (selected-object (selected-object scroller)))
- (when top-object
- (draw-object scroller top-object :selectable-p selectable-p)
- (when selected-object
- (setf (selected-object scroller) selected-object)
- (with-focused-view scroller
- (highlight selected-object)))))))
-
-
- ;;; Simple graphic objects. These are objects that know how to draw themselves on to views
- ;;; and respond to mouse clicks. Their instance variables are:
- ;;;
- ;;; Instance Variable Description
- ;;; ================= ===========
- ;;; top Location of top of object - set :before draw method
- ;;; left Location of left of object - set :before draw method
- ;;;
- ;;; The methods that they must respond to are:
- ;;;
- ;;; Method Description
- ;;; ====== ===========
- ;;;
- ;;; size-h horizontal size of object - must be provided by subclass!
- ;;; size-v vertical size of object - must be provided by subclass!
- ;;; bottom bottom of object
- ;;; right right of object
- ;;; draw draws object - must be specialized by subclass!
- ;;;
- ;;; border-XXXX border sizes around object
- ;;;
- ;;; descendants selectable objects "inside" this one
- ;;; selectable-p determines if this object can be clicked
- ;;; click-event-handler handles click inside of object
- ;;; highlight draws (xors) dots around object
- ;;; select called when object is selected (default highlights object)
- ;;; deselect called when a selected object is deselected
- ;;;
-
- (defclass simple-graphic-object ()
- ((top :accessor top)
- (left :accessor left)))
-
- (defmethod bottom ((obj simple-graphic-object))
- (+ (top obj) (size-v obj)))
-
- (defmethod right ((obj simple-graphic-object))
- (+ (left obj) (size-h obj)))
-
- (defmethod draw :before ((obj simple-graphic-object) left top)
- (setf (top obj) top)
- (setf (left obj) left))
-
-
- (defmethod head ((obj simple-graphic-object))
- (floor (size-h obj) 2))
-
- (defmethod base ((obj simple-graphic-object))
- (floor (size-v obj) 2))
-
-
- (defmethod descendants ((obj simple-graphic-object))
- '())
-
- (defmethod selectable-p ((obj simple-graphic-object))
- nil)
-
- (defmethod click-event-handler ((obj simple-graphic-object) where)
- (if (and (<= (left obj) (point-h where) (right obj))
- (<= (top obj) (point-v where) (bottom obj)))
- (or (some #'(lambda (descendant) (click-event-handler descendant where)) ; click in
- (descendants obj)) ; descendant
- (and (selectable-p obj) obj)) ; click in me
- nil)) ; click not in me
-
- (defmethod highlight ((obj simple-graphic-object))
- (let* ((left (left obj))
- (right (right obj))
- (mid (floor (+ left right) 2)))
- (rlet ((pen-state :penstate))
- (#_GetPenState pen-state)
- (#_PenNormal)
- (#_PenSize 3 3)
- (#_PenMode #.(position :patXor *pen-modes*))
- (dolist (h `(,left ,mid ,right))
- (dolist (v `(,(bottom obj) ,(top obj)))
- (#_MoveTo h v)
- (#_Line 1 0)))
- (#_SetPenState pen-state))))
-
- (defmethod select ((obj simple-graphic-object))
- (highlight obj))
-
- (defmethod deselect ((obj simple-graphic-object))
- (highlight obj))
-
- (defmethod border ((obj simple-graphic-object))
- 0)
-
- (defmethod border-h ((obj simple-graphic-object))
- (border obj))
-
- (defmethod border-v ((obj simple-graphic-object))
- (border obj))
-
- (defmethod border-top ((obj simple-graphic-object))
- (border-v obj))
-
- (defmethod border-bottom ((obj simple-graphic-object))
- (border-v obj))
-
- (defmethod border-left ((obj simple-graphic-object))
- (border-h obj))
-
- (defmethod border-right ((obj simple-graphic-object))
- (border-h obj))
-
-
- ;;; graphic-objects are simple-graphic-objects that cache their sizes and have borders
-
- (defclass graphic-object (simple-graphic-object)
- ((size-h :accessor size-h)
- (size-v :accessor size-v)
- (generation :accessor generation :initform -1)))
-
- (defmethod update-if-necessary ((obj graphic-object))
- (let ((graphic-generation (graphic-generation *the-view*)))
- (unless (= (generation obj) graphic-generation)
- (setf (generation obj) graphic-generation)
- (multiple-value-bind (size-h size-v) (compute-size obj)
- (setf (size-h obj) (+ (border-left obj) size-h (border-right obj)))
- (setf (size-v obj) (+ (border-top obj) size-v (border-bottom obj)))))))
-
- (defmethod size-h :before ((obj graphic-object))
- (update-if-necessary obj))
-
- (defmethod size-v :before ((obj graphic-object))
- (update-if-necessary obj))
-
- (defmethod draw ((obj graphic-object) left top)
- (draw-obj obj (+ left (border-left obj)) (+ top (border-top obj))))
-
- ;;; string objects
-
- (defclass string-object (simple-graphic-object)
- ((display-string :accessor display-string)))
-
- (defmethod initialize-instance ((obj string-object) &key (string "*Unspecified*"))
- (call-next-method)
- (setf (display-string obj) (princ-to-string string)))
-
- (defmethod size-h ((obj string-object))
- (+ (border-left obj)
- (string-width (display-string obj))
- (border-right obj)))
-
- (defmethod size-v ((obj string-object))
- (+ (border-top obj)
- (line-height)
- (border-bottom obj)))
-
- (defmethod base ((obj string-object))
- (+ (border-top obj)
- (line-ascent)))
-
- (defmethod draw ((obj string-object) left top)
- (#_MoveTo (+ left (border-left obj))
- (+ top (base obj)))
- (with-pstrs ((string (display-string obj)))
- (#_DrawString string)))
-
- ;;; small-string-objects are string objects in a smaller font size
-
- (defclass small-string-object (string-object) ())
-
- (defmacro with-font-size (font-size &body body)
- (let ((txSize (gensym "txSize")))
- `(let ((,txSize (font-size *the-view*)))
- (unwind-protect (progn
- (#_TextSize ,font-size)
- ,@body)
- (#_TextSize ,txSize)))))
-
- (defmethod small-font-size ((self small-string-object))
- (max 9 (ceiling (* (font-size *the-view*) 3) 4)))
-
- (defmethod size-h ((self small-string-object))
- (with-font-size (small-font-size self)
- (call-next-method)))
-
- (defmethod size-v ((self small-string-object))
- (with-font-size (small-font-size self)
- (call-next-method)))
-
- (defmethod draw ((self small-string-object) left top)
- (let ((base (base self))
- (border-left (border-left self)))
- (with-font-size (small-font-size self)
- (#_MoveTo (+ left border-left) (+ top base))
- (with-pstrs ((string (display-string self)))
- (#_DrawString string)))))
-
- (defmethod base ((self small-string-object))
- (with-font-size (small-font-size self)
- (call-next-method)))
-
- ;;; Sequences are composite objects.
-
- (defclass sequence-object (graphic-object)
- ((objects :accessor objects :initarg :objects)
- (offset :accessor offset)))
-
- (defmethod empty-size ((self sequence-object))
- 0)
-
- (defmethod empty-h ((self sequence-object))
- (empty-size self))
-
- (defmethod empty-v ((self sequence-object))
- (empty-size self))
-
- (defmethod alignment ((self sequence-object))
- #'(lambda (o)
- (declare (ignore o))
- 0))
-
- (defmethod offset :before ((self sequence-object))
- "Force a size calculation if this hasn't been done yet"
- (update-if-necessary self))
-
- ;;; horizontal-sequence is a subclass of graphic-object consisting of a
- ;;; sequence of other graphic objects, which will be laid out in a
- ;;; line. An empty horizontal-sequence has zero size.
-
- (defclass horizontal-sequence (sequence-object) ())
-
- (defmethod gap-h ((self horizontal-sequence))
- "Gap between objects"
- (round (line-height)
- 2))
-
- (defmethod compute-size ((self horizontal-sequence))
- (let* ((objects (objects self))
- (alignment (alignment self))
- (offset (reduce #'max objects :key alignment :initial-value 0)))
- (setf (offset self) offset)
- (if (null objects)
- (values (empty-h self) (empty-v self))
- (values (+ (reduce #'+ objects :key #'size-h) ; horizontal size is sum of object's size
- (* (1- (length objects)) (gap-h self))) ; plus gap
- (+ offset
- (reduce #'max objects
- :key #'(lambda (obj)
- (- (size-v obj) (funcall alignment obj)))))))))
-
- (defmethod draw-obj ((self horizontal-sequence) left top)
- (let ((objects (objects self))
- (alignment (alignment self))
- (gap-h (gap-h self))
- (offset (offset self)))
- (when objects
- (flet ((pos (obj)
- (+ top (- offset (funcall alignment obj)))))
- (draw (first objects) left (pos (first objects)))
- (incf left (+ gap-h (size-h (first objects))))
- (dolist (obj (rest objects))
- (draw obj left (pos obj))
- (incf left (+ (size-h obj) gap-h)))))))
-
- ;;; vertical-sequence is a subclass of graphic-object consisting of an aligned
- ;;; sequence of vertical objects.
-
- (defclass vertical-sequence (sequence-object) ())
-
- (defmethod gap-v ((self vertical-sequence))
- "Gap between objects"
- (line-leading))
-
- (defmethod empty ((self vertical-sequence))
- (ceiling (* 2 (line-height)) 3))
-
- (defmethod compute-size ((self vertical-sequence))
- (let* ((objects (objects self))
- (alignment (alignment self))
- (offset (reduce #'max objects :key alignment :initial-value 0)))
- (setf (offset self) offset)
- (if (null objects)
- (values (empty-h self) (empty-v self))
- (values (+ offset
- (reduce #'max objects
- :key #'(lambda (obj)
- (- (size-h obj) (funcall alignment obj)))))
- (+ (reduce #'+ objects :key #'size-v) ; vertical size is sum of object's size
- (* (1- (length objects)) (gap-v self))))))) ; plus gap
-
- (defmethod draw-obj ((self vertical-sequence) left top)
- (let ((objects (objects self))
- (alignment (alignment self))
- (gap-v (gap-v self))
- (offset (offset self)))
- (when objects
- (flet ((pos (obj)
- (+ left (- offset (funcall alignment obj)))))
- (draw (first objects) (pos (first objects)) top)
- (incf top (+ gap-v (size-v (first objects))))
- (dolist (obj (rest objects))
- (draw obj (pos obj) top)
- (incf top (+ (size-v obj) gap-v)))))))
-
- (defmethod base ((self vertical-sequence))
- (let ((objects (objects self)))
- (if (= (length objects) 1)
- (+ (border-top self) (base (first objects)))
- (call-next-method))))
-
- ;;; tree objects consist of a vertical sequence consisting of the root and
- ;;; a horizontal sequence of subtrees
-
- (defclass tree-object (vertical-sequence) ())
-
- (defclass subtrees-object (horizontal-sequence) ())
-
- (defmethod initialize-instance ((tree tree-object) &key root subtrees)
- (call-next-method)
- (setf (objects tree) (list root (make-instance 'subtrees-object :objects subtrees))))
-
- (defmethod root ((tree tree-object))
- (first (objects tree)))
-
- (defmethod subtrees ((tree tree-object))
- (objects (second (objects tree))))
-
- (defmethod gap-v ((tree tree-object))
- (line-height))
-
- (defmethod selectable-p ((tree tree-object))
- "The whole tree (as an object) is selectable"
- t)
-
- (defmethod descendants ((tree tree-object))
- "The selectable components of a tree are its subtrees"
- (subtrees tree))
-
- (defmethod base ((tree tree-object))
- (+ (border-top tree) (base (root tree))))
-
- (defmethod alignment ((tree tree-object))
- #'head)
-
- (defmethod head ((tree tree-object))
- (+ (border-left tree)
- (offset tree)))
-
- (defmethod head ((obj subtrees-object))
- "The head of a subtree is the average of the head of its first and last subtrees"
- (let ((objects (objects obj)))
- (if (null objects)
- (call-next-method)
- (let* ((lefttree (first objects))
- (righttree (car (last objects)))
- (rightobj-head (- (size-h obj) (border-left obj) (border-right obj)
- (- (size-h righttree) (head righttree)))))
- (+ (border-left obj) (floor (+ (head lefttree) rightobj-head) 2))))))
-
- (defmethod draw ((tree tree-object) left top)
- "The default methods position and draw the objects, so all we have to do is draw the lines"
- (declare (ignore left top))
- (call-next-method)
- (let* ((root (root tree))
- (root-bottom (+ (line-leading) (bottom root)))
- (root-head (+ (left root) (head root))))
- (dolist (subtree (subtrees tree))
- (let ((subtree-top (- (top subtree) 2))
- (subtree-head (+ (left subtree) (head subtree))))
- (when (< -2 (- root-head subtree-head) 2)
- (setf subtree-head root-head))
- (#_MoveTo root-head root-bottom)
- (#_LineTo subtree-head subtree-top)))))
-
-
- (defun list-to-tree (list)
- "Translates a list into a tree object"
- (if (consp list)
- (make-instance 'tree-object
- :root (list-to-tree (first list))
- :subtrees (mapcar #'list-to-tree (rest list)))
- (make-instance 'string-object :string list)))
-
- ;;; drawtree draws a tree in its own window
-
- (defun drawtree (tree &key selectable-p)
- "Draws a tree in its own tree window"
- (if (listp tree)
- (setf tree (list-to-tree tree)))
- (let ((front-window (front-window)))
- (unless (and (typep *tree-window* 'graphic-window)
- (wptr *tree-window*))
- (setf *tree-window* (make-instance 'graphic-window
- :window-title "Tree Window"
- :view-size #@(200 200))))
- (draw-object *tree-window* tree :selectable-p selectable-p)
- (window-show *tree-window*)
- (window-select *tree-window*)
- (window-select front-window)))
-
-
- ;;; bracket-mixin adds a bracket surrounding the object
- ;;;
-
- (defclass bracket-mixin () ())
-
- (defmethod border-h ((self bracket-mixin))
- (max (1+ (floor (line-height) 3))
- (call-next-method)))
-
- (defmethod border-v ((self bracket-mixin))
- (max 3
- (call-next-method)))
-
- (defmethod draw :after ((self bracket-mixin) left top)
- (declare (ignore left top))
- (let ((bracket-width (floor (line-height) 3)))
- (#_MoveTo (+ (left self) bracket-width) (1+ (top self)))
- (#_Line (- bracket-width) 0)
- (#_LineTo (left self) (1- (bottom self)))
- (#_Line bracket-width 0)
- (#_MoveTo (- (right self) bracket-width) (1+ (top self)))
- (#_Line bracket-width 0)
- (#_LineTo (right self) (1- (bottom self)))
- (#_Line (- bracket-width) 0)))
-
-
- ;;; brace-mixin adds a parenthesis or brace around the object
- ;;;
-
-
- (defclass brace-mixin () ())
-
- (defmethod brace-size ((self brace-mixin))
- (max 2
- (floor (line-height) 8)))
-
- (defmethod border-h ((self brace-mixin))
- (max (+ 2 (* 2 (brace-size self)))
- (call-next-method)))
-
- (defmethod border-v ((self brace-mixin))
- (max (brace-size self)
- (call-next-method)))
-
- (defmethod draw :after ((self brace-mixin) left top)
- (declare (ignore left top))
- (let* ((brace-size (brace-size self))
- (left (+ (left self) brace-size))
- (top (+ (top self) brace-size))
- (right (- (right self) brace-size))
- (bottom (- (bottom self) brace-size))
- (mid (round (+ top bottom) 2)))
- (#_MoveTo left top)
- (#_Line brace-size (- brace-size))
- (#_MoveTo left top)
- (#_LineTo left (- mid brace-size))
- (#_Line (- brace-size) brace-size)
- (#_Line brace-size brace-size)
- (#_LineTo left bottom)
- (#_Line brace-size brace-size)
- (#_MoveTo right top)
- (#_Line (- brace-size) (- brace-size))
- (#_MoveTo right top)
- (#_LineTo right (- mid brace-size))
- (#_Line brace-size brace-size)
- (#_Line (- brace-size) brace-size)
- (#_LineTo right bottom)
- (#_Line (- brace-size) brace-size)))
-
-
- ;;; angle-bracket-mixin adds an angle-bracket around the object
- ;;;
-
-
- (defclass angle-bracket-mixin () ())
-
- (defmethod angle-bracket-size ((self angle-bracket-mixin))
- (floor (* 2 (line-height)) 3))
-
- (defmethod border-h ((self angle-bracket-mixin))
- (max (+ 2 (angle-bracket-size self))
- (call-next-method)))
-
- (defmethod draw :after ((self angle-bracket-mixin) left top)
- (declare (ignore left top))
- (let* ((angle-bracket-size (angle-bracket-size self))
- (left (+ (left self) angle-bracket-size))
- (top (top self))
- (right (- (right self) angle-bracket-size))
- (bottom (bottom self))
- (mid (round (+ top bottom) 2)))
- (#_MoveTo left top)
- (#_LineTo (- left angle-bracket-size) mid)
- (#_LineTo left bottom)
- (#_MoveTo right top)
- (#_LineTo (+ right angle-bracket-size) mid)
- (#_LineTo right bottom)))
-
-
- ;;; box-mixin adds a rectangle box around the object
- ;;;
-
- (defclass box-mixin () ())
-
- (defmethod draw :after ((self box-mixin) left top)
- (declare (ignore left top))
- (rlet ((r :rect
- :topleft (make-point (left self) (1+ (top self)))
- :bottomright (make-point (right self) (1- (bottom self)))))
- (#_FrameRect r)))
-
- ;;; Here is the AV interface.
- ;;;
- ;;; avm-object ---> index-object
- ;;; |
- ;;; |-> avm-bracket-object ---> A o
- ;;; | V b
- ;;; |-> j
- ;;; | p e
- ;;; |-> a c
- ;;; | i t
- ;;; ... r s
-
- ;;; index-objects are the cute little boxed integers that decorate AVMs
-
- (defclass index-object (box-mixin small-string-object) ())
-
- (defmethod border-h ((self index-object))
- (max 2 (call-next-method)))
-
- (defmethod border-bottom ((self index-object))
- (max 1 (call-next-method)))
-
- (defmethod border-top ((self index-object))
- (max 2 (call-next-method)))
-
- ;;; avm-bracket-objects are the bracketted parts of the AVM.
-
- (defclass avm-bracket-object (bracket-mixin vertical-sequence) ())
-
- (defclass avm-object (horizontal-sequence)
- ((avm-bracket :reader avm-bracket :initform (make-instance 'avm-bracket-object))
- (index :accessor index :initform nil)))
-
- (defclass avm-pair (horizontal-sequence) ())
-
- (defmethod alignment ((self avm-object))
- #'size-v)
-
- (defmethod alignment ((self avm-pair))
- #'base)
-
- (defmethod base ((self avm-pair))
- (+ (border-top self)
- (offset self)))
-
- (defmethod base ((self avm-object))
- (+ (border-top self) (base (avm-bracket self))))
-
- (defmethod base ((self avm-bracket-object))
- (if (null (objects self))
- (+ (floor (size-v self) 2) (line-descent))
- (call-next-method)))
-
- (defmethod objects ((self avm-object))
- (let ((index (index self))
- (avm-bracket (avm-bracket self)))
- (if index
- (list index avm-bracket)
- (list avm-bracket))))
-
- (defmethod gap-h ((self avm-object))
- "Gap between index and avm-bracket"
- (round (line-height)
- 4))
-
- (defmethod gap-h ((self avm-pair))
- (string-width " "))
-
- (defmethod empty-h ((self avm-bracket-object))
- 4)
-
- (defmethod empty-v ((self avm-bracket-object))
- (line-ascent))
-
- (defmethod set-avm-pairs ((self avm-object) avm-pairs)
- (setf (objects (avm-bracket self)) avm-pairs))
-
- (defun make-avm-pair (attribute value)
- "Make the AV pair"
- (make-instance 'avm-pair
- :objects `(,(make-instance 'string-object
- :string (format nil "~a =" attribute))
- ,value)))
-
-
-